import '@/utils/libs/validation/validation.js' // 表单验证类
import * as UniUtils from '@/utils/uni'
import store from '@/store/index'

const Utils = {
  /**
   * 用于拼接接口返回的 base64 参数
   */
  base64Img(base64) {
    if (!base64) return null
    return 'data:image/png;base64,' + base64
  },

  /**
   * 两个浮点数求和
   * @param {Number} num1
   * @param {Number} num2
   * @returns {Number}
   */
  mathAdd(num1, num2) {
    let r1, r2
    try {
      r1 = num1.toString().split('.')[1].length
    } catch (e) {
      r1 = 0
    }
    try {
      r2 = num2.toString().split('.')[1].length
    } catch (e) {
      r2 = 0
    }
    const m = Math.pow(10, Math.max(r1, r2))
    return Math.round(num1 * m + num2 * m) / m
  },

  /**
   * 两个数相减
   * @param {Number} num1
   * @param {Number} num2
   * @returns {Number}
   */
  mathSubtract(num1, num2) {
    let r1, r2
    try {
      r1 = num1.toString().split('.')[1].length
    } catch (e) {
      r1 = 0
    }
    try {
      r2 = num2.toString().split('.')[1].length
    } catch (e) {
      r2 = 0
    }
    const m = Math.pow(10, Math.max(r1, r2))
    const n = r1 >= r2 ? r1 : r2
    return (Math.round(num1 * m - num2 * m) / m).toFixed(n)
  },

  /**
   * 两个数相乘
   * @param {Number} num1
   * @param {Number} num2
   * @returns {Number}
   */
  mathMultiply(num1, num2) {
    let m = 0
    const s1 = num1.toString()
    const s2 = num2.toString()
    try {
      m += s1.split('.')[1].length
    } catch (e) {
      // console.log('mathMultiply err', e)
    }
    try {
      m += s2.split('.')[1].length
    } catch (e) {
      // console.log('mathMultiply err', e)
    }
    return (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) / Math.pow(10, m)
  },

  /**
   * 两个浮点数相除
   * @param {Number} num1
   * @param {Number} num2
   * @returns {Number}
   */
  mathDivide(num1, num2) {
    let t1, t2
    try {
      t1 = num1 ? num1.toString().split('.')[1].length : 0
    } catch (e) {
      t1 = 0
    }
    try {
      t2 = num2 ? num2.toString().split('.')[1].length : 0
    } catch (e) {
      t2 = 0
    }

    const r1 = num1 ? Number(num1.toString().replace('.', '')) : num1
    const r2 = num2 ? Number(num2.toString().replace('.', '')) : num2
    return (r1 / r2) * Math.pow(10, t2 - t1)
  },

  /**
   * 解析 time 为字符串
   * @param {(Object|string|number)} time
   * @param {string} cFormat
   * @returns {string | null}
   */
  parseTime(time, cFormat) {
    if (arguments.length === 0 || !time) {
      return null
    }
    const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
    let date
    if (typeof time === 'object') {
      date = time
    } else {
      if (typeof time === 'string') {
        if (/^[0-9]+$/.test(time)) {
          // support "1548221490638"
          time = parseInt(time)
        } else {
          // support safari
          // https://stackoverflow.com/questions/4310953/invalid-date-in-safari
          time = time.replace(new RegExp(/-/gm), '/')
        }
      }

      if (typeof time === 'number' && time.toString().length === 10) {
        time = time * 1000
      }
      date = new Date(time)
    }
    const formatObj = {
      y: date.getFullYear(),
      m: date.getMonth() + 1,
      d: date.getDate(),
      h: date.getHours(),
      i: date.getMinutes(),
      s: date.getSeconds(),
      a: date.getDay()
    }
    const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
      const value = formatObj[key]
      // Note: getDay() returns 0 on Sunday
      if (key === 'a') {
        return ['日', '一', '二', '三', '四', '五', '六'][value]
      }
      return value.toString().padStart(2, '0')
    })
    return time_str
  },

  /**
   * 用于格式化时间
   * @param {number} time: new Date('2020-02-02')
   * @param {string} option: true / false
   * @returns {string} option === true 返回 "2020-02-02 08:00:00", option === false 返回 ""2月2日8时0分""
   */
  formatTime(time, option) {
    if (('' + time).length === 10) {
      time = parseInt(time) * 1000
    } else {
      time = +time
    }
    const d = new Date(time)
    const now = Date.now()

    const diff = (now - d) / 1000

    if (diff < 30) {
      return '刚刚'
    } else if (diff < 3600) {
      // less 1 hour
      return Math.ceil(diff / 60) + '分钟前'
    } else if (diff < 3600 * 24) {
      return Math.ceil(diff / 3600) + '小时前'
    } else if (diff < 3600 * 24 * 2) {
      return '1天前'
    }
    if (option) {
      return Utils.parseTime(time, option)
    } else {
      return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
    }
  },

  /**
   * 用于获取 url 中 ？后面的参数
   * @param {string} url: https://www.iconfont.cn/search/index?searchType=icon&q=列表&page=5
   * @returns {Object} {searchType: "icon", q: "列表", page: "5"}
   */
  param2Obj(url) {
    const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
    if (!search) {
      return {}
    }
    const obj = {}
    const searchArr = search.split('&')
    searchArr.forEach(v => {
      const index = v.indexOf('=')
      if (index !== -1) {
        const name = v.substring(0, index)
        const val = v.substring(index + 1, v.length)
        obj[name] = val
      }
    })
    return obj
  },

  /**
   * 反转 Object 内部的 key 和 value
   * @param {Object} obj: { a: 1, b: 2 }
   * @returns {Object} { 1: a, 2: b }
   */
  flipObject(obj) {
    const temp = {}
    const keys = Object.keys(obj)
    if (keys.length > 0) {
      Object.values(obj).map((item, i) => {
        temp[item] = keys[i]
      })
      return temp
    }
    return {}
  },

  /**
   * 用于获取 url 中 ？后面的参数
   * @param {string} url: https://www.iconfont.cn/search/index?searchType=icon&q=列表&page=5
   * @returns {Object} {searchType: "icon", q: "列表", page: "5"}
   */
  paramConvertObj(url) {
    if (!url) return {}
    const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
    if (!search) return {}
    const obj = {}
    const searchArr = search.split('&')
    searchArr.forEach(v => {
      const index = v.indexOf('=')
      if (index !== -1) {
        const name = v.substring(0, index)
        const val = v.substring(index + 1, v.length)
        obj[name] = val
      }
    })
    return obj
  },

  /**
   * 金额按千位逗号分割
   * @param {number} s 需要格式化的金额数值.
   * @param {number} type 判断格式化后的金额是否需要小数位.
   * @returns {string} 返回格式化后的数值字符串. formatMoney("12345.675910", 3)，返回 12,345.676
   */
  formatMoney(s, type) {
    if (/[^0-9\.]/.test(s)) {
      return '0'
    }
    if (s === null || s === '') {
      return '0'
    }
    s = s.toString().replace(/^(\d*)$/, '$1.')
    s = (s + '00').replace(/(\d*\.\d\d)\d*/, '$1')
    s = s.replace('.', ',')
    var re = /(\d)(\d{3},)/
    while (re.test(s)) {
      s = s.replace(re, '$1,$2')
    }
    s = s.replace(/,(\d\d)$/, '.$1')
    if (type === 0) {
      // 不带小数位(默认是有小数位)
      var a = s.split('.')
      if (a[1] === '00') {
        s = a[0]
      }
    }
    return s.split('.')[0]
  },

  /**
   * 货币格式
   * @param {number} number 需要格式化的金额数值.
   * @param {number} places 小数点后几位
   * @param {string} symbol 前置符号 $
   * @param {string} thousand 千分位显示什么符号默认
   * @param {string} decimal 小数点替换符号
   * @returns {string} ￥327,100.23，如果没有小数，￥327,100
   */
  formatMoneys(number, places, symbol, thousand, decimal) {
    const oriNum = number
    number = number || 0
    places = !isNaN((places = Math.abs(places))) ? places : 2
    symbol = symbol !== undefined ? symbol : '￥'
    thousand = thousand || ','
    decimal = decimal || '.'
    var negative = number < 0 ? '-' : ''
    var i = parseInt((number = Math.abs(+number || 0).toFixed(places)), 10) + ''
    var j = (j = i.length) > 3 ? j % 3 : 0
    var formatMoney =
      symbol +
      negative +
      (j ? i.substr(0, j) + thousand : '') +
      i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousand) +
      (places
        ? decimal +
          Math.abs(number - i)
            .toFixed(places)
            .slice(2)
        : '')
    if (oriNum || oriNum === 0) {
      if (oriNum.toString().indexOf('.') === -1) {
        return formatMoney.substr(0, formatMoney.length - 3)
      }
    } else {
      return ''
    }
    // if (oriNum.toString().indexOf('.') === -1) {
    //   return formatMoney.substr(0, formatMoney.length - 3)
    // }
    return formatMoney
  },

  /**
   * 用于对象取参，可返回设定的默认值（数据源,参数,默认值）
   * @param {*} origin 需要取值的对象
   * @param {String} param 对象中的key名
   * @param {*} defaultData 如果取不到，默认返回的值
   * @returns {*} origin.param || defaultData
   *
   * 例子1： const origin = { res: { data: { token: 123 } }}
   * format(origin, 'res.data.token')
   *
   * 例子2： const origin = [{ a: 1 }, { b: 2 }]
   * format(origin, '0.a')
   * format(origin, '1.b')
   * format(origin, 'length')
   *
   * const origin = [{ state: [1,2,3] }]
   * format(origin, '0.state.1')
   * format(origin, '0.state.length')
   */
  format(origin, param, defaultData = null) {
    // origin == null - 除去 undefined 和 null 的存在
    // Utils.isObject(param) === 'object' - 防止判断到空数组 []
    if (origin == null || typeof param === 'undefined' || Utils.isObject(param) === 'object') {
      return defaultData
    }

    if (Utils.isNumber(param)) param = String(param) // 做到 param 可传入 Number
    const arr = Utils.isArray(param) ? param : param.split('.')
    const finalParam = arr.shift()

    if (param.length === 0) {
      // console.log('2 ', origin, arr, finalParam)
      if (finalParam) return typeof origin[finalParam] === 'undefined' ? defaultData : origin[finalParam]
      return typeof origin === 'undefined' ? defaultData : origin
    }

    //  console.log('1 ', origin[finalParam], arr, defaultData)
    return typeof origin[finalParam] === 'undefined' ? defaultData : Utils.format(origin[finalParam], arr, defaultData)
  },

  /**
   * 防抖函数
   * @param {*} fn 方法
   * @param {*} wait 再次执行时间
   * @param {*} isImmediate 是否立即执行
   * @returns
   */
  debounce(fn, wait = 500, isImmediate = true) {
    let timerId = null
    let flag = true
    if (isImmediate) {
      return function () {
        clearTimeout(timerId)
        if (flag) {
          fn.apply(this, arguments)
          flag = false
        }
        timerId = setTimeout(() => {
          flag = true
        }, wait)
      }
    }

    return function () {
      clearTimeout(timerId)
      timerId = setTimeout(() => {
        fn.apply(this, arguments)
      }, wait)
    }
  },

  /**
   * 深拷贝一个对象
   * @param {Object} source
   * @returns {Object}
   */
  deepClone(source) {
    if (!source && typeof source !== 'object') {
      throw new Error('error arguments', 'deepClone')
    }
    const targetObj = source.constructor === Array ? [] : {}
    Object.keys(source).forEach(keys => {
      if (source[keys] && typeof source[keys] === 'object') {
        targetObj[keys] = Utils.deepClone(source[keys])
      } else {
        targetObj[keys] = source[keys]
      }
    })
    return targetObj
  },

  /**
   * 判断一个对象是否为空
   * @param {Object} val
   * @returns {Boolean}
   */
  isEmpty(val) {
    if (val == null) return true // null or undefined
    if (typeof val === 'boolean') return false
    if (typeof val === 'number') return !val
    if (val instanceof Error) return val.message === ''

    switch (Object.prototype.toString.call(val)) {
      // String or Array
      case '[object String]':
      case '[object Array]':
        return !val.length

      // Map or Set or File
      case '[object File]':
      case '[object Map]':
      case '[object Set]': {
        return !val.size
      }
      // Plain Object
      case '[object Object]': {
        return !Object.keys(val).length
      }
    }
    return false
  },

  /**
   * 判断是否为对象
   * @param {*} param
   * @returns {Boolean}
   */
  isType(param, type) {
    return Object.prototype.toString.call(param) === `[object ${type}]`
  },

  /**
   * 判断是否为数组
   * @param {*} param
   * @returns {Boolean}
   */
  isArray(param) {
    return Utils.isType(param, 'Array')
  },

  /**
   * 判断是否为数字
   * @param {*} param
   * @returns {Boolean}
   */
  isNumber(param) {
    return Utils.isType(param, 'Number')
  },

  /**
   * 判断是否为字符串
   * @param {*} param
   * @returns {Boolean}
   */
  isString(param) {
    return Utils.isType(param, 'String')
  },

  /**
   * 判断是否为对象
   * @param {*} param
   * @returns {Boolean}
   */
  isObject(param) {
    return Utils.isType(param, 'Object')
  }
}

const openCustom = (param, callback) => {
  if (Utils.isEmpty(param)) return

  // 方式一 ： 跳转到 web-view
  if (param.openType === 'webview') {
    const query = encodeURIComponent(JSON.stringify(param))
    open('/extend/web-view/web-view?query=' + query)
    callback && callback()
    return
  }

  // 方式二 ： 预览图片
  if (param.openType === 'preview') {
    uni.previewImage({ current: param.img, urls: [param.img] })
    callback && callback()
    return
  }

  // 弹窗
  if (param.openType === 'msg') {
    const msgObj = param.msg || {}
    if (Utils.isEmpty(msgObj)) return
    msg(msgObj.type, msgObj.text, msgObj.obj || null)
    callback && callback()
    return
  }

  // 打开新的小程序
  if (param.openType === 'navigateToMiniProgram') {
    const appId = param.appId || null
    const path = param.url || null

    if (!appId) return msg('text', '后台未配置appId')
    if (!path) return msg('text', '后台未配置path')

    let finalPath = path
    const account = store.state.Login.mobile || ''
    if (path.indexOf('?webViewUrl=') > -1) {
      const account = store.state.Login.mobile
      const arr = path.split('?webViewUrl=')
      const basePath = arr[0] || null
      const otherParam = arr[1] || null

      const query = encodeURIComponent(
        JSON.stringify({
          url: account ? `${otherParam}?account=${account}` : `${otherParam}`
        })
      )

      finalPath = `${basePath}?query=${query}`
    }

    uni.navigateToMiniProgram({
      appId: appId,
      path: finalPath,
      extraData: account ? { account: account, token: store.getters.token, ...param.extraData } : param.extraData || {},
      success(res) {
        console.log('navigateToMiniProgram res', res)
      }
    })

    callback && callback()
    return
  }

  // 方式三 ： 跳转本地界面
  const openType = (param && param.openType) || 'navigateTo'
  const url = (param && param.url) || null
  url && open(url, openType || 'nt')
  callback && callback()
}

export default {
  ...UniUtils,
  ...Utils,
  openCustom
}
